# -*- coding: UTF-8 -*-

import datetime
import time
import os
from common import TradeSide
from AlgoClass import AlgoOrder,OrderBook,AlgoStatus,printLog,islessThanOneLotSize,AlgoExecStatic
from config import ALGO_DIR
from strategySignal import ALL_ACCOUNT_MOVE_POSITIONS

'''
OneQuant DEMO:算法母单导入示例
应用场景:将策略给出的调仓信号通过本程序写入算法母单文件，进行算法下单
重要:在测试环境使用时,请修改 config.py及strategySignal.py文件中相应的 Algo_DIR 目录及账号信息。
     相关使用规范请查阅【算法单导入使用手册】
    1:用户在写入母单导入文件时,请确保传入的本地订单号custAlgoNo 为32位无符号整型(uint32)且为当天唯一

'''

def insertAlgoOrder(fd,algoOrder:AlgoOrder):

    order_str=algoOrder.startAlgoCSVFormat()
    fd.write(order_str+"\n")     # 写入AlgoStart文件 
    printLog("[%-5s]算法单号:%d,股票:%s,数量:%d" % ( TradeSide.getTradeSideName(algoOrder.tradeSide),algoOrder.custAlgoNo,algoOrder.symbol,algoOrder.targetVol),flush = True)

def cancelAlgoOrder(fd,algoExecStatic:AlgoExecStatic):

    stop_str=algoExecStatic.stopAlgoCSVFormat()
    fd.write(stop_str+"\n")      # 写入AlgoStop文件
    printLog('撤单:' + stop_str,flush = True)

def init_order_cancel_files(order_file,cancel_file):
    if not os.path.exists(order_file):
        with open(order_file,'w') as f:
            f.write("updTime,custAlgoNo,acctType,acct,symbol,tradeSide,targetVol,targetAmt,algoId,algoParam" + "\n")
        printLog('创建算法母单导入文件:' + order_file)
    if not os.path.exists(cancel_file):
        with open(cancel_file,'w') as f:
            f.write("updTime,custAlgoNo,acctType,acct" + "\n")
        printLog('创建算法母单撤单文件:' + cancel_file)


def generateAlgo_beg_end_Time(duration_mins):

    today = datetime.datetime.now().strftime('%Y-%m-%d')
    marketOpenTime_M= datetime.datetime.strptime( today + ' 093000', '%Y-%m-%d %H%M%S')  # 早市开盘
    marketOpenTime_A= datetime.datetime.strptime( today + ' 130000', '%Y-%m-%d %H%M%S')  # 午市开盘
    beginTime= max(datetime.datetime.now(),marketOpenTime_M)
    endTime= beginTime + datetime.timedelta(seconds=60*duration_mins)
    endTime_str= endTime.strftime('%H%M%S')
    beginTime_str=beginTime.strftime('%H%M%S')
    if beginTime_str<"113000" and endTime_str>"113000" and endTime_str<"150000": # 整个午休
        endTime=beginTime + datetime.timedelta(seconds=60*duration_mins+3600*1.5)
    elif beginTime_str>="113000" and beginTime_str<"130000": # 部分午休
        endTime=beginTime + datetime.timedelta(seconds=(marketOpenTime_A-beginTime).seconds + 60*duration_mins)

    return beginTime_str,endTime.strftime('%H%M%S')

if __name__ == '__main__':
       
    # 初始化下单文件及撤单文件,订单簿orderbook,启动orderbook
    today= datetime.datetime.now().strftime("%Y%m%d")
    order_file =os.path.join(ALGO_DIR,    'algoStart_' + today + '.csv')
    cancel_file=os.path.join(ALGO_DIR,     'algoStop_' + today + '.csv')

    init_order_cancel_files(order_file,cancel_file)
    
    orderbook=OrderBook(ALGO_DIR,today)
    orderbook.init_history()     # 读取当天已回写的文件数据
    orderbook.run()              # 后台运行 读取各个回写文件

    executeMins=5  # 算法执行时长
    
    try:
        with open(order_file,'a',buffering=1) as order_f,open(cancel_file,'a',buffering=1) as cancel_f:
            
            for (acctType,acct),positionMoves in ALL_ACCOUNT_MOVE_POSITIONS.items():
                printLog("将调仓生成算法单" )
                for pm in positionMoves:
                    symbol,tradeSide,targetVol = pm

                    algoOrder_param= {"acct":acct,"acctType":acctType,"symbol":symbol,"targetVol":targetVol,"tradeSide":tradeSide.value}
                    # 安智类算法参数 
                    # algoOrder_param["algoId"] = "OQ-TWAP"
                    # algoOrder_param["limitPrice"] = 0
                    # algoOrder_param["minAmount"] = 0
                    # algoOrder_param["participateRate"] = 0.1
                    # algoOrder_param["targetAmt"] = 0

                    # 迅投类算法参数 
                    # algoOrder_param["algoId"] = "XT-TWAP"
                    # algoOrder_param["limitPrice"] = 0
                    # algoOrder_param["MinAmount"] = 0
                    # algoOrder_param["PartRate"] = 0.1

                    # 卡方类算法参数
                    algoOrder_param["algoId"] = "KF-TWAP-PLUS" 
                    algoOrder_param["priceF"] = 0

                    beginTime,endTime= generateAlgo_beg_end_Time(executeMins)
                    algoOrder_param['beginTime']    =beginTime
                    algoOrder_param['endTime']      =endTime
                    updTime=datetime.datetime.now().strftime("%H%M%S.%f")
                    algoOrder_param['custAlgoNo']= int(float(updTime)*pow(10,3)) + int(algoOrder_param['symbol'][0:6])   # custAlgoNo 需为 32位整型范围
                    algoOrder_param['updTime'] = updTime

                    algoOrder = AlgoOrder().parseAlgoOrder(algoOrder_param)
                    orderbook.custAlgoNo2AlgoOrders[algoOrder.custAlgoNo]=algoOrder
                    insertAlgoOrder(order_f,algoOrder)

            time.sleep(5)

            running=True
            while running:
                totalAlgoOrders= len(orderbook.custAlgoNo2AlgoExecStatic)
                runninAlgoOrders=len(orderbook.custAlgoNo2AlgoExecStatic_running)
                completeAlgoOrders=0
                errorAlgoOrders=0
                expiredAlgoOrders=0             # 算法运行到期但目标未完成：比如设置了限价或者参与率或者出现涨跌停，目标未能完成
                termiatedAlgoOrders=0           # 客户自行停止的算法
                for custAlgoNo,algoExecStatic in orderbook.custAlgoNo2AlgoExecStatic.items():
                    symbol=algoExecStatic.symbol
                    if symbol=="" and custAlgoNo in orderbook.custAlgoNo2AlgoOrders:
                        symbol=orderbook.custAlgoNo2AlgoOrders[custAlgoNo].symbol
                    remaindQty=algoExecStatic.targetVol-algoExecStatic.filledQty
                    remaindMsg=""
                    if islessThanOneLotSize(algoExecStatic.symbol,remaindQty):
                        remaindMsg="[零碎股]" 
                    if AlgoStatus.isFinalStatus(AlgoStatus(algoExecStatic.status)):
                        if algoExecStatic.status== AlgoStatus.Completed.value:
                            completeAlgoOrders +=1
                        if algoExecStatic.status== AlgoStatus.Error.value:
                            errorAlgoOrders +=1
                        if algoExecStatic.status== AlgoStatus.Expired.value:
                            expiredAlgoOrders +=1
                        if algoExecStatic.status== AlgoStatus.Terminated.value:
                            termiatedAlgoOrders +=1
                    printLog(("[%-10s][%-4s]算法单号:%d(%s),股票:%s,执行进度:%.2f,剩余数量:%d" ) % (AlgoStatus(algoExecStatic.status).name, TradeSide.getTradeSideName(algoOrder.tradeSide),algoExecStatic.custAlgoNo,algoExecStatic.algoInstanceId,symbol,algoExecStatic.progress*100,remaindQty) + remaindMsg)
                        
                printLog(("算法母单总数:%d,完成数:%d,正在运行数:%d,过期未完成数:%d,错误失败数:%d,终止数:%d") % (totalAlgoOrders,completeAlgoOrders,runninAlgoOrders,expiredAlgoOrders,errorAlgoOrders,termiatedAlgoOrders))
                running=len(orderbook.custAlgoNo2AlgoExecStatic_running)>0
                time.sleep(10)
                # 测试停止运行中算法实例
                # for custAlgoNo,algoExecStatic in orderbook.custAlgoNo2AlgoExecStatic.items():
                #     if not AlgoStatus.isFinalStatus(AlgoStatus(algoExecStatic.status)):
                #         cancelAlgoOrder(cancel_f,algoExecStatic)

    except Exception as e:
        printLog(str(e))
        raise(e)

    finally:
        orderbook.stop()
